﻿#include "Etat.h"
#include "Etiquette.h"
#include "ListeEtats.h"
#include "Automate.h"

#include <string>
#include <exception>
#include <iostream>
#include <sstream>
#include <cctype>
#include <fstream>

#define PAUSE_SYSTEM	system("pause"); 
#define CLEAR_SYSTEM	system("cls"); 

using std::exception;
using std::stringstream;
using std::cerr;
using std::cout;
using std::cin;
using std::endl;

template<typename T>
bool getCin(T& result)
{
	string input;
	getline(cin, input);
	stringstream inputStream(input);
	if (inputStream >> result)
		return true;
	return false;
}

bool getCin(string& result)
{
	getline(cin, result);
	return true;
}


/*
	Menu principal
*/

const unsigned BUILD_OPTION = 1;
const unsigned REQUIS_OPTION = 2;
const unsigned TEST_OPTION = 3;
const unsigned EXIT_OPTION = 0;
const unsigned ERROR_OPTION = 666;

void buildOption();
void testerRequisOption();
void testOption();
void printAutomate();
void onQuit();

Automate* automatePrincipal = new Automate();

void main(void) try {
	//Appeler onQuit() à la fin de l'exécution
	atexit(onQuit);

	bool quitter = false;
	unsigned option = 0;

	
	cout << "Bienvenue dans le TP2 de l'equipe 1631079-1632734-1640463" << endl;
	cout << endl;
	PAUSE_SYSTEM;

	do {
		CLEAR_SYSTEM;
		automatePrincipal->print();
		cout << endl;

		cout << "(" << BUILD_OPTION << ")Construire l'automate" << endl;
		cout << "(" << REQUIS_OPTION << ")Tester requis" << endl;
		cout << "(" << TEST_OPTION << ")Tester cible" << endl;
		cout << "(" << EXIT_OPTION << ")Quitter" << endl;
		cout << "Votre choix: ";
		if (!getCin(option))
			option = ERROR_OPTION;		

		cout << endl;
		switch (option)
		{
		case BUILD_OPTION:
			buildOption();
			break;
		case REQUIS_OPTION:
			testerRequisOption();
			break;
		case TEST_OPTION:
			testOption();
			break;
		case EXIT_OPTION:
			cout << "Vous avez quitte l'application normalement" << endl;
			PAUSE_SYSTEM;
			quitter = true;
			break;
		default:
			cout << "Choix errone. Veuillez reessayer" << endl;
			PAUSE_SYSTEM;
			break;
		}
	} while (!quitter);
	
	
	
}
catch (std::exception& e) {
	cerr << e.what();
}


/*Build Options:
Voici toutes les informations concernants les options de construction de l'automate
NOTE: Ces options ne servent que pour outils de déboggage, le TP préfère construire
les automates à partir d'un fichier!
*/
const unsigned ADD_STATE_OPTION = 1;
const unsigned REMOVE_STATE_OPTION = 2;
const unsigned ADD_TRANSITION_OPTION = 3;
const unsigned REMOVE_TRANSITION_OPTION = 4;
const unsigned BUILD_FROM_FILE_OPTION = 5;
const unsigned EXPORT_TO_FILE_OPTION = 6;

void addStateOption();
void removeStateOption();
void addTransitionOption();
void removeTransitionOption();
void buildFromFileOption();
void exportToFileOption();

void buildOption()
{
	bool quitter = false;
	while (!quitter)
	{
		CLEAR_SYSTEM;
		automatePrincipal->print();
		cout << endl;

		unsigned choix = 0;
		cout << "(" << ADD_STATE_OPTION << ")Ajouter Etat" << endl;
		cout << "(" << REMOVE_STATE_OPTION << ")Retirer Etat" << endl;
		cout << "(" << ADD_TRANSITION_OPTION << ")Ajouter Transition" << endl;
		cout << "(" << REMOVE_TRANSITION_OPTION << ")Retirer Transition" << endl;
		cout << "(" << BUILD_FROM_FILE_OPTION << ")Construire automate d'un fichier" << endl;
		cout << "(" << EXPORT_TO_FILE_OPTION << ")Construire un fichier a partir de l'automate" << endl;
		cout << "(" << EXIT_OPTION << ")Revenir au menu" << endl;
		cout << "Votre choix: ";
		if (!getCin(choix))
			choix = ERROR_OPTION;
		cout << endl;

		switch (choix)
		{
		case ADD_STATE_OPTION:
			addStateOption();
			break;
		case REMOVE_STATE_OPTION:
			removeStateOption();
			break;
		case ADD_TRANSITION_OPTION:
			addTransitionOption();
			break;
		case REMOVE_TRANSITION_OPTION:
			removeTransitionOption();
			break;
		case BUILD_FROM_FILE_OPTION:
			buildFromFileOption();
			break;
		case EXPORT_TO_FILE_OPTION:
			exportToFileOption();
			break;
		case EXIT_OPTION:
			quitter = true;
			break;
		default:
			cout << "Choix errone" << endl;
			PAUSE_SYSTEM;
			break;
		}
	}
}


void addStateOption()
{
	unsigned choix = 0;
	unsigned id = ERROR_OPTION;
	do {
		CLEAR_SYSTEM;
		automatePrincipal->print();
		cout << endl;

		cout << "Choisir l'ID de l'etat a ajouter" << endl;
		cout << "Votre choix: ";
		if (!getCin(choix))
			choix = ERROR_OPTION;
		cout << endl;
	} while (choix == ERROR_OPTION);
	id = choix;

	bool initial = false;
	do {
		CLEAR_SYSTEM;
		automatePrincipal->print();
		cout << endl;

		cout << "Etat initial? (Maximum 1 par automate)" << endl;
		cout << "(1)Vrai" << endl;
		cout << "(0)Faux" << endl;
		cout << "Votre choix: ";
		if (!getCin(choix))
			choix = ERROR_OPTION;
		cout << endl;

		switch (choix)
		{
		case 1:
			initial = true;
			break;
		case 0:
			initial = false;
			break;
		default:
			cout << "Choix errone" << endl;
			choix = ERROR_OPTION;
			PAUSE_SYSTEM;
			break;
		}
	} while (choix == ERROR_OPTION);

	bool terminal = false;
	do {
		CLEAR_SYSTEM;
		automatePrincipal->print();
		cout << endl;

		cout << "Etat terminal?" << endl;
		cout << "(1)Vrai" << endl;
		cout << "(0)Faux" << endl;
		cout << "Votre choix: ";
		if (!getCin(choix))
			choix = ERROR_OPTION;
		cout << endl;

		switch (choix)
		{
		case 1:
			terminal = true;
			break;
		case 0:
			terminal = false;
			break;
		default:
			cout << "Choix errone" << endl;
			choix = ERROR_OPTION;
			PAUSE_SYSTEM;
			break;
		}
	} while (choix == ERROR_OPTION);

	automatePrincipal->ajouterEtat(id, initial, terminal);
}

void removeStateOption()
{
	unsigned choix = 0;
	unsigned id = ERROR_OPTION;
	do {
		CLEAR_SYSTEM;
		automatePrincipal->print();
		cout << endl;

		cout << "Choisir l'ID de l'etat a retirer" << endl;
		cout << "Votre choix: ";
		if (!getCin(choix))
			choix = ERROR_OPTION;
		cout << endl;
	} while (choix == ERROR_OPTION);
	id = choix;

	automatePrincipal->retirerEtat(id);
}

void addTransitionOption()
{
	unsigned choix = 0;
	unsigned id = ERROR_OPTION;
	do {
		CLEAR_SYSTEM;
		automatePrincipal->print();
		cout << endl;

		cout << "Choisir l'ID de l'etat source" << endl;
		cout << "Votre choix: ";
		if (!getCin(choix))
			choix = ERROR_OPTION;
		cout << endl;
	} while (choix == ERROR_OPTION);
	id = choix;

	Etat* etatSource = automatePrincipal->getEtat(id);

	if (etatSource == nullptr)
	{
		cout << "Etat source invalide, retour au menu" << endl;
		PAUSE_SYSTEM;
		return;
	}
		
	Etiquette etiquette;

	string symboles;
	do {
		CLEAR_SYSTEM;
		etatSource->print();
		cout << endl;

		cout << "Ajouter des symboles a la transition (0 pour annuler)" << endl;
		cout << "Votre choix: ";
		if (!getCin(symboles))
			symboles = "";
		cout << endl;

		if (symboles == "0")
			return;

		//On ajoute tous les symboles alphabetiquettes aux étiquettes
		for (unsigned i = 0; i < symboles.size(); ++i)
		{
			char tempChar = symboles[i];
			if (isalpha(tempChar))
				etiquette.addSymbole(tempChar);
		}
	} while (symboles == "");

	do {
		CLEAR_SYSTEM;
		automatePrincipal->print();
		cout << endl;

		cout << "Choisir l'ID de l'etat destination" << endl;
		cout << "Votre choix: ";
		if (!getCin(choix))
			choix = ERROR_OPTION;
		cout << endl;
	} while (choix == ERROR_OPTION);
	id = choix;

	Etat* etatDestination = automatePrincipal->getEtat(id);
	if (etatDestination == nullptr)
	{
		cout << "Etat destination invalide, retour au menu" << endl;
		PAUSE_SYSTEM;
		return;
	}

	etatSource->ajouterTransition(etiquette, etatDestination);
}

void removeTransitionOption() {
	unsigned choix = 0;
	unsigned id = ERROR_OPTION;

	//Choix de l'etat source
	do {
		CLEAR_SYSTEM;
		automatePrincipal->print();
		cout << endl;

		cout << "Choisir l'ID de l'etat source de la transition a retirer" << endl;
		cout << "Votre choix: ";
		if (!getCin(choix))
			choix = ERROR_OPTION;
		cout << endl;
	} while (choix == ERROR_OPTION);
	id = choix;

	Etat* etatSource = automatePrincipal->getEtat(id);
	if (etatSource == nullptr)
	{
		cout << "Etat invalide. Retour au menu." << endl;
		PAUSE_SYSTEM;
		return;
	}

	//Choix de la destination
	do {
		CLEAR_SYSTEM;
		etatSource->print();
		cout << endl;

		cout << "Choisir l'ID de l'etat destination de la transition a retirer" << endl;
		cout << "Votre choix: ";
		if (!getCin(choix))
			choix = ERROR_OPTION;
		cout << endl;
	} while (choix == ERROR_OPTION);
	id = choix;

	Etat* etatDestination = automatePrincipal->getEtat(id);
	if (etatDestination == nullptr)
	{
		cout << "Etat invalide. Retour au menu." << endl;
		PAUSE_SYSTEM;
		return;
	}

	etatSource->retirerTransition(id);
}

void buildFromFileOption() {
	
	string symboles;
	do {
		CLEAR_SYSTEM;

		cout << "Entrer le nom du fichier a lire avec extension" << endl;
		cout << "Votre choix: ";
		if (!getCin(symboles))
			symboles = "";
		cout << endl;
	} while (symboles == "");
	
	Automate* automateTemp;
	try{ automateTemp = new Automate(symboles); }
	catch (std::exception& e) {
		cout << "Erreur dans la lecture du fichier:" << endl;
		cout << e.what() << endl;
		cout << endl;
		cout << "Retour au menu." << endl;
		PAUSE_SYSTEM;
		return;
	}

	delete automatePrincipal;
	automatePrincipal = automateTemp;
	
	
}

void exportToFileOption() {
	string symboles;
	do {
		CLEAR_SYSTEM;

		cout << "Entrer le nom du fichier ou l'automate sera ecrit" << endl;
		cout << "Votre choix: ";
		if (!getCin(symboles))
			symboles = "";
		cout << endl;
	} while (symboles == "");

	automatePrincipal->genererFichierAutomate(symboles);
}

/*Tester Requis Options:
Voici où on met les options pour tester les 3 requis qui se font dans
ce TP
*/

const unsigned IS_DETERMINISTE_OPTION = 1;
const unsigned DERTERMINISER_OPTION = 2;
const unsigned READ_MOT_OPTION = 3;

void isDeterministeOption();
void determiniserOption();
void readMotOption();

void testerRequisOption()
{
	bool quitter = false;
	while (!quitter)
	{
		CLEAR_SYSTEM;
		automatePrincipal->print();
		cout << endl;

		unsigned choix = 0;
		cout << "(" << IS_DETERMINISTE_OPTION << ")Tester si l'automate actuel est deterministe" << endl;
		cout << "(" << DERTERMINISER_OPTION << ")Determiniser l'automate (non fonctionel)" << endl;
		cout << "(" << READ_MOT_OPTION << ")Lire un mot dans l'automate actuel" << endl;
		cout << "(" << EXIT_OPTION << ")Revenir au menu" << endl;
		cout << "Votre choix: ";
		if (!getCin(choix))
			choix = ERROR_OPTION;
		cout << endl;

		switch (choix)
		{
		case IS_DETERMINISTE_OPTION:
			isDeterministeOption();
			break;
		//case DERTERMINISER_OPTION:
		//	determiniserOption();
		//	break;
		case READ_MOT_OPTION:
			readMotOption();
			break;
		case EXIT_OPTION:
			quitter = true;
			break;
		default:
			cout << "Choix errone" << endl;
			PAUSE_SYSTEM;
			break;
		}
	}
}

void isDeterministeOption()
{
	CLEAR_SYSTEM;
	automatePrincipal->print();
	cout << endl;

	if (automatePrincipal->estDeterministe())
		cout << "Cet automate est deterministe." << endl;
	else
		cout << "Cet automate n'est pas deterministe." << endl;

	PAUSE_SYSTEM;
}

void determiniserOption()
{
	CLEAR_SYSTEM;

	cout << "Ancien automate: " << endl;
	automatePrincipal->print();
	cout << endl;

	 PAUSE_SYSTEM;

	 Automate autoTemp = automatePrincipal->determiniser();
	 autoTemp.genererFichierAutomate("Test");
	 CLEAR_SYSTEM;

	 cout << "Automate derterminiser (enregistre dans le fichier 'Test.txt') :" << endl;
	 autoTemp.print();
	 cout << endl;
	 
	 PAUSE_SYSTEM;


}

void readMotOption()
{
	string motAlire;
	do 
	{
	
	CLEAR_SYSTEM;
	automatePrincipal->print();
	cout << endl;

	cout << "Entrez le mot a lire (0 pour annuler): " << endl;
	cout << "Votre choix: ";
	if (!getCin(motAlire))
		motAlire = "";
	cout << endl;
	if (motAlire == "0")
		return;
	} while (motAlire == "");

	try{
		if (automatePrincipal->reconnaitreMot(motAlire))
			cout << "Le mot a ete reconnu" << endl;
		else
			cout << "Le mot n'a pas ete reconnu (ou l'automate est non-deterministe)" << endl;
		PAUSE_SYSTEM;
		readMotOption();
	}
	catch (std::exception& e)
	{
		cout << "Erreur dans la reconnaisance du mot:" << endl;
		cout << e.what() << endl;
		PAUSE_SYSTEM;
		readMotOption();
	}
}

void testOption() {
	int id;
	Etat* etatChoisi = automatePrincipal->getEtatInitial();

	bool quitter = false;
	do {
		CLEAR_SYSTEM;
		unsigned choix = 0;
		automatePrincipal->print();
		cout << "Etat choisi: ";
		if (etatChoisi != nullptr)
			cout << etatChoisi->getId();
		else
			cout << "Aucun";
		cout << endl << endl;
		cout << "(1) Choisir un etat source" << endl;
		cout << "(2) Cibler un caractere" << endl;
		cout << "(3) Cibler tout" << endl;
		cout << "(0) Quitter" << endl;
		cout << "Votre choix: ";
		if (!getCin(choix))
			choix = ERROR_OPTION;
		cout << endl;
			
		ListeEtats liste;

		switch (choix)
		{
		case 1:
			do {
				CLEAR_SYSTEM;
				automatePrincipal->print();
				cout << endl;

				cout << "Veuillez choisir un ID valide pour l'etat source (-1 pour retour)" << endl;
				cout << "Votre ID: ";
				if (!getCin(id))
					id = ERROR_OPTION;
				cout << endl;

				if (id == -1)
					break;
				if (id == ERROR_OPTION)
				{
					cout << "ID invalide" << endl;
					PAUSE_SYSTEM
						continue;
				}

				etatChoisi = automatePrincipal->getEtat(id);
			} while (etatChoisi == nullptr);
			break;
		case 2:
			do {
				char c;
				CLEAR_SYSTEM;
				etatChoisi->print();
				cout << endl;
				cout << "Veuillez entrer le caractere a cibler (0 pour revenir au menu)" << endl;
				cout << "Votre caractere: ";
				if (!getCin(c))
					c = ' ';
				cout << endl;
				if (c == '0')
					break;
				if (c == ' ')
					continue;

				liste = etatChoisi->cible(c);

				cout << "Liste des etats cibles:" << endl;

				for (unsigned i = 0; i < liste.size(); ++i)
				{
					Etat* tempEtatB;
					tempEtatB = liste.getEtat(i);
					cout << "Etat: ID " << tempEtatB->getId() << endl;
				}

				cout << endl;
				PAUSE_SYSTEM

			} while (true);
			break;
		case 3:
			CLEAR_SYSTEM;
			etatChoisi->print();
			cout << endl;

			liste = etatChoisi->cible();

			cout << "Liste des etats cibles:" << endl;

			for (unsigned i = 0; i < liste.size(); ++i)
			{
				Etat* tempEtatB;
				tempEtatB = liste.getEtat(i);
				cout << "Etat: ID " << tempEtatB->getId() << endl;
			}
			cout << endl;
			PAUSE_SYSTEM;
			break;
		default:
			quitter = true;
			break;
		}


	} while (!quitter);
	
	

}

void onQuit()
{
	delete automatePrincipal;
	automatePrincipal = nullptr;
}

